package com.activenetwork.util;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;

@SuppressWarnings({ "unchecked" })
public class JaxbUtil {
	
	private static JAXBContext jaxbContext = null;
	private static Marshaller marshaller = null;
	
	/**
	 * Build the XML and write changed data in JAXB object into XML
	 * @param xmlDocument
	 * @param packagePath
	 * @param rootNodeElement
	 * 
	 * @param <T>
	 */
	public static <T extends Object> void buildXml(File xmlDocument, String packagePath, JAXBElement<T> rootNodeElement) {
		
		try {
			jaxbContext = JAXBContext.newInstance(packagePath);
			marshaller = jaxbContext.createMarshaller();
			marshaller.marshal(rootNodeElement, new FileOutputStream(xmlDocument));
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (JAXBException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * Binding the XML to corresponding java class and return the java instance 
	 * representing the root node
	 * 
	 * @param <T>
	 * @param xmlDocument
	 * @param packagePath
	 * @return
	 */
	public static <T extends Object> T buildObject(File xmlDocument, String packagePath) {
		JAXBElement<T> testDataElement = null;
		try {
			JAXBContext jaxbContext = JAXBContext.newInstance(packagePath);
			Unmarshaller unMarshaller = jaxbContext.createUnmarshaller();
			testDataElement = (JAXBElement<T>) unMarshaller.unmarshal(xmlDocument);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return testDataElement.getValue();
	}
	/**
	 * Binding the XML to corresponding java class and return the java instance 
	 * representing the root node
	 * 
	 * @param <T>
	 * @param bindingPath
	 * @param xmlFile
	 * @param rootClass the java class binding to the root node type
	 * @return
	 */
	public static <T extends Object> T buildObject(File xmlFile, String bindingPath, Class<T> rootClass) {
		
		// Get the root node object from DOM
		Element root = null;
		try {
			DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
			dbf.setNamespaceAware(true);
			DocumentBuilder db = dbf.newDocumentBuilder();
			Document doc = db.parse(xmlFile);
			root = doc.getDocumentElement();
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		// Unmarshal from root node
		JAXBElement<T> XMLRootNode = null;
		try {
			JAXBContext jaxbContext = JAXBContext.newInstance(bindingPath);
			Unmarshaller unMarshaller = jaxbContext.createUnmarshaller();
			XMLRootNode = unMarshaller.unmarshal(root, rootClass);
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		return XMLRootNode.getValue();
	}
	/**
	 * Write the change in java binding class back into XML
	 * This method is to overwrite all the xml from root node
	 * 
	 * @param bindingPath
	 * @param xmlFile
	 * @param instance the java binding class instance representing the root node
	 */
	public static void updateXML(File xmlFile, String bindingPath, Object instance) {
		
		// Get the root node object from DOM
		Element root = null;
		try {
			DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
			dbf.setNamespaceAware(true);
			DocumentBuilder db = dbf.newDocumentBuilder();
			Document doc = db.parse(xmlFile);
			root = doc.getDocumentElement();
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		// Unmarshal from root node
		JAXBElement<Object> XMLRootNode = null;
		try {
			JAXBContext jaxbContext = JAXBContext.newInstance(bindingPath);
			Unmarshaller unMarshaller = jaxbContext.createUnmarshaller();
			Class<? extends Object> clazz = instance.getClass();
			XMLRootNode = (JAXBElement<Object>) unMarshaller.unmarshal(root, clazz);
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		// Update the root jaxb object
		XMLRootNode.setValue(instance);
		
		// Marshal
		try {
			JAXBContext jaxbContext = JAXBContext.newInstance(bindingPath);
			Marshaller marshaller = jaxbContext.createMarshaller();
			marshaller.marshal(XMLRootNode, xmlFile);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
}
